home *** CD-ROM | disk | FTP | other *** search
Text File | 1999-06-26 | 33.0 KB | 1,171 lines | [TEXT/CWIE] |
- /*
- File: LiveFastStartServer.c by Sam Bushell
- -- based on OTSimpleServerHTTP.c and HackTV.c
-
- Contains: Implementation of the simple HTTP server sample.
-
- Written by: Quinn "The Eskimo!"
-
- Copyright: © 1997 by Apple Computer, Inc., all rights reserved.
-
- Change History (most recent first):
-
- 08/27/97 - Rich Kubota - Included call to DoNegotiateIPReuseAddrOption
- so that on each endpoint, the IP level ReuseAddr option
- is enabled so that on immediate relaunch of the application,
- the designated IP addresses could be reused without IP
- protecting them until the timeout occurs.
-
- You may incorporate this sample code into your applications without
- restriction, though the sample code has been provided "AS IS" and the
- responsibility for its operation is 100% yours. However, what you are
- not permitted to do is to redistribute the source as "DSC Sample Code"
- after having made changes. If you're going to re-distribute the source,
- we require that you make it clear in the source that the code was
- descended from Apple Sample Code, but that you've made changes.
-
- =====================================================================
-
- Let me make this clear: this source was descended (sic) from Apple Sample Code,
- but I've made changes. Horrible changes.
-
- Sam Bushell
- MacHack, June 1999
-
- */
-
- /////////////////////////////////////////////////////////////////////
- // The OT debugging macros in <OTDebug.h> require this variable to
- // be set.
-
- #ifndef qDebug
- #define qDebug 1
- #endif
-
- /////////////////////////////////////////////////////////////////////
- // Pick up all the standard OT stuff.
-
- #include <OpenTransport.h>
-
- /////////////////////////////////////////////////////////////////////
- // Pick up all the OT TCP/IP stuff.
-
- #include <OpenTptInternet.h>
-
- /////////////////////////////////////////////////////////////////////
- // Pick up the OTDebugBreak and OTAssert macros.
-
- //#include <OTDebug.h>
- #define OTAssert(x,y) if(!(y)) printf(x);
-
- /////////////////////////////////////////////////////////////////////
- // Pick up the various Thread Manager APIs.
-
- #include <Threads.h>
-
- /////////////////////////////////////////////////////////////////////
-
- #include <stdio.h>
- #include <string.h>
-
- #include <Endian.h>
- #include <Menus.h>
- #include <Script.h>
- #include <ImageCompression.h>
- #include <QuickTimeComponents.h>
- #include <Files.h>
- #include <Events.h>
- #include <MacWindows.h>
- #include <Resources.h>
- #include <NumberFormatting.h>
- #include <Strings.h>
-
- /////////////////////////////////////////////////////////////////////
- // Pick up our own prototype.
-
- #include "LiveFastStartServer.h"
-
- /////////////////////////////////////////////////////////////////////
- // OTDebugStr is not defined in any OT header files, but it is
- // exported by the libraries, so we define the prototype here.
-
- extern pascal void OTDebugStr(const char* str);
-
- /////////////////////////////////////////////////////////////////////
- // When this boolean is to set true, this module assumes that the
- // host application is trying to quit and all the threads created by
- // this module start dying. See the associated comment in the
- // YieldingNotifier routine.
-
- extern Boolean gQuitNow = false;
-
- // RRK Comments added 8/27/97
- // DoNegotiateIPReuseAddrOption is defined in the file
- // EnableIPReuseAddrSample.c. This call uses the OTOptionManagement function
- // to set the IP level ResuseAddr option so that an IP address can be
- // reused on immediate relaunch of the application.
- extern OSStatus DoNegotiateIPReuseAddrOption(EndpointRef ep, Boolean enableReuseIPMode);
-
- /////////////////////////////////////////////////////////////////////
-
- static pascal void YieldingNotifier(EndpointRef ep, OTEventCode code,
- OTResult result, void* cookie)
- // This simple notifier checks for kOTSyncIdleEvent and
- // when it gets one calls the Thread Manager routine
- // YieldToAnyThread. Open Transport sends kOTSyncIdleEvent
- // whenever it's waiting for something, eg data to arrive
- // inside a sync/blocking OTRcv call. In such cases, we
- // yield the processor to some other thread that might
- // be doing useful work.
- //
- // The routine also checks the gQuitNow boolean to see if the
- // the host application wants us to quit. This roundabout technique
- // avoids a number of problems including:
- //
- // 1. Threads stuck inside OT synchronous calls -- You can't just
- // call DisposeThread on a thread that's waiting for an OT
- // synchronous call to complete. Trust me, it would be bad!
- // Instead, this routine calls OTCancelSynchronousCalls to get
- // out of the call. The given error code (userCanceledErr)
- // propagates out to the caller, which causes the calling
- // thread to eventually terminate.
- // 2. Threads holding resources -- You can't just DisposeThread
- // a networking thread because it might be holding resouces,
- // like memory or endpoints, that need to be cleaned up properly.
- // Cancelling the thread in this way causes the thread's own
- // code to clean up those resources just like it would for any
- // any other error.
- //
- // I could have used a more sophisticated mechanism to support
- // quitting (such as a boolean per thread, or returning some
- // "thread object" to which the application can send a "cancel"
- // message, but this way is easy and works just fine for this
- // simple sample
- {
- #pragma unused(result)
- #pragma unused(cookie)
- OSStatus junk;
-
- //DebugStr( "\p YieldingNotifier" );
-
- switch (code) {
- case kOTSyncIdleEvent:
- junk = YieldToAnyThread();
- OTAssert("YieldingNotifier: YieldToAnyThread failed", junk == noErr);
-
- if (gQuitNow) {
- junk = OTCancelSynchronousCalls(ep, userCanceledErr);
- OTAssert("YieldingNotifier: Failed to cancel", junk == noErr);
- }
- break;
- default:
- // do nothing
- break;
- }
- }
-
- /////////////////////////////////////////////////////////////////////
-
- static void SetDefaultEndpointModes(EndpointRef ep)
- // This routine sets the supplied endpoint into the default
- // mode used in this application. The specifics are:
- // blocking, synchronous, and using synch idle events with
- // the standard YieldingNotifier.
- {
- OSStatus junk;
-
- //DebugStr( "\p SetDefaultEndpointModes" );
- junk = OTSetBlocking(ep);
- OTAssert("SetDefaultEndpointModes: Could not set blocking", junk == noErr);
- junk = OTSetSynchronous(ep);
- OTAssert("SetDefaultEndpointModes: Could not set synchronous", junk == noErr);
- junk = OTInstallNotifier(ep, &YieldingNotifier, ep);
- OTAssert("SetDefaultEndpointModes: Could not install notifier", junk == noErr);
- junk = OTUseSyncIdleEvents(ep, true);
- OTAssert("SetDefaultEndpointModes: Could not use sync idle events", junk == noErr);
- }
-
- /////////////////////////////////////////////////////////////////////
-
- static OSStatus OTSndQ(EndpointRef ep, void *buf, size_t nbytes)
- // My own personal wrapper around the OTSnd routine that cleans
- // up the error result.
- {
- OTResult bytesSent;
-
- //DebugStr("\p OTSnd before" );
- bytesSent = OTSnd(ep, buf, nbytes, 0);
- //DebugStr("\p OTSnd after" );
- if (bytesSent >= 0) {
-
- // Because we're running in synchronous blocking mode, OTSnd
- // should not return until it has sent all the bytes unless it
- // gets an error. If it does, we want to hear about it.
- OTAssert("OTSndQ: Not enough bytes sent", bytesSent == nbytes);
-
- return (noErr);
- } else {
- return (bytesSent);
- }
- }
-
- /////////////////////////////////////////////////////////////////////
-
- static OSErr FSReadQ(short refNum, long count, void *buffPtr)
- // My own wrapper for FSRead. Whose bright idea was it for
- // it to return the count anyway!
- {
- OSStatus err;
- long tmpCount;
-
- tmpCount = count;
- err = FSRead(refNum, &count, buffPtr);
-
- OTAssert("FSReadQ: Did not read enough bytes", (err != noErr) || (count == tmpCount));
-
- return (err);
- }
-
- /////////////////////////////////////////////////////////////////////
-
- static Boolean StringHasSuffix(const char *str, const char *suffix)
- // Returns true if the end of str is suffix.
- {
- Boolean result;
-
- result = false;
- if ( OTStrLength(str) >= OTStrLength(suffix) ) {
- if ( OTStrEqual(str + OTStrLength(str) - OTStrLength(suffix) , suffix) ) {
- result = true;
- }
- }
-
- return (result);
- }
-
- static OSStatus ExtractRequestedFileName(const char *buffer,
- char *fileName, char *mimeType)
- // Assuming that buffer is a C string contain an HTTP request,
- // extract the name of the file that's being requested.
- // Also check to see if the file has one of the common suffixes,
- // and set mimeType appropriately.
- //
- // Obviously this routine should use Internet Config to
- // map the file type/creator/extension to a MIME type,
- // but I don't want to complicate the sample with that code.
- {
- OSStatus err;
-
- // Default the result to empty.
- fileName[0] = 0;
-
- // Scan the request looking for the fileName. Obviously this is not
- // a very good validation of the request, but this is an OT sample,
- // not an HTTP one. Also note that we require HTTP/1.0, but some
- // ancient clients might just generate "GET %s<cr><lf>"
-
- (void) sscanf(buffer, "GET %s HTTP/1.0", fileName);
-
- // If the file name is still blank, scanf must have failed.
- // Note that I don't rely on the result from scanf because in a
- // previous life I learnt to mistrust it.
-
- if (fileName[0] == 0) {
- err = -1;
- } else {
-
- // So the request is cool. Normalise the file name.
- // Requests for the root return "index.html".
-
- if ( OTStrEqual(fileName, "/") ) {
- OTStrCopy(fileName, "index.html");
- }
-
- // Remove the prefix slash. Note that we don't deal with
- // "slashes" embedded in the fileName, so we don't handle
- // any directories other than the root. This would be
- // easy to do, but again this is not an HTTP sample.
-
- if ( fileName[0] == '/' ) {
- BlockMoveData(&fileName[1], &fileName[0], OTStrLength(fileName));
- }
-
- // Set mimeType based on the file's suffix.
-
- if ( StringHasSuffix(fileName, ".html") ) {
- OTStrCopy(mimeType, "text/html");
- } else if ( StringHasSuffix(fileName, ".gif") ) {
- OTStrCopy(mimeType, "image/gif");
- } else if ( StringHasSuffix(fileName, ".jpg") ) {
- OTStrCopy(mimeType, "image/jpeg");
- } else if ( StringHasSuffix(fileName, ".mov") ) {
- OTStrCopy(mimeType, "video/quicktime");
- } else {
- OTStrCopy(mimeType, "text/plain");
- }
- err = noErr;
- }
-
- #if qDebug
- printf("ExtractRequestedFileName: Returning %d, “%s”, “%s”\n", err, fileName, mimeType);
- #endif
-
- return (err);
- }
-
- /////////////////////////////////////////////////////////////////////
-
- // The worker thread reads characters one at a time from the endpoint
- // and uses the following state machine to determine when the request is
- // finished. For HTTP/1.0 requests, the request is terminated by
- // two consecutive CR LF pairs. Each time we read one of the appropriate
- // characters we increment the state until we get to kDone, at which
- // point we go off to process the request.
-
- enum {
- kWorkerWaitingForCR1,
- kWorkerWaitingForLF1,
- kWorkerWaitingForCR2,
- kWorkerWaitingForLF2,
- kWorkerDone
- };
-
- // This is the size of the transfer buffer that each worker thread
- // allocates to read file system data and write network data.
-
- enum {
- kTransferBufferSize = 4096
- };
-
- // WorkerContext holds the information needed by a worker endpoint to
- // operate. A WorkerContext is created by the listener endpoint
- // and passed as the thread parameter to the worker thread. If the
- // listener successfully does this, it's assumed that the worker
- // thread has taken responsibility for freeing the context.
-
- struct WorkerContext {
- EndpointRef worker;
- short vRefNum;
- long dirID;
- };
- typedef struct WorkerContext WorkerContext, *WorkerContextPtr;
-
- // The two buffers hold standard HTTP responses. The first is the
- // default text we spit out when we get an error. The second is
- // the header that we use when we successfully field a request.
- // Again note that this sample is not about HTTP, so these responses
- // are probably not particularly compliant to the HTTP protocol.
-
- char gDefaultOutputText[] = "HTTP/1.0 200 OK\15\12Content-Type: text/html\15\12\15\12<H1>Say what!</H1><P>\15\12Error Number (%d), Error Text (%s)";
- //char gHTTPHeader[] = "HTTP/1.0 200 OK\15\12Content-Type: %s\15\12\15\12";
- char gHTTPHeader[] = "HTTP/1.0 200 OK\15\12Pragma: no-cache\15\12Cache-Control: no-cache\15\12Content-Type: %s\15\12\15\12";
-
- /////////////////////////////////////////////////////////////////////
-
- static OSStatus ReadHTTPRequest(EndpointRef worker, char *buffer)
- // This routine reads the HTTP request from the worker endpoint,
- // using the state machine described above, and puts it into the
- // indicated buffer. The buffer must be at least kTransferBufferSize
- // bytes big.
- //
- // This is pretty feeble
- // code (reading data one byte at a time is bad for performance),
- // but it works and I'm not quite sure how to fix it. Perhaps
- // OTCountDataBytes?
- //
- // Also, the code does not support with requests bigger than
- // kTransferBufferSize. In practise, this isn't a problem.
- {
- OSStatus err;
- long bufferIndex;
- int state;
- char ch;
- OTResult bytesReceived;
- OTFlags junkFlags;
-
- OTAssert("ReadHTTPRequest: What endpoint?", worker != nil);
- OTAssert("ReadHTTPRequest: What buffer?", buffer != nil);
-
- bufferIndex = 0;
- state = kWorkerWaitingForCR1;
- do {
- bytesReceived = OTRcv(worker, &ch, sizeof(char), &junkFlags);
- if (bytesReceived >= 0) {
- OTAssert("ReadHTTPRequest: Didn't read the expected number of bytes", bytesReceived == sizeof(char));
-
- err = noErr;
-
- // Put the character into the buffer.
-
- buffer[bufferIndex] = ch;
- bufferIndex += 1;
-
- // Check that we still have space to include our null terminator.
-
- if (bufferIndex >= kTransferBufferSize) {
- err = -1;
- }
-
- // Do the magic state machine. Note the use of
- // hardwired numbers for CR and LF. This is correct
- // because the Internet standards say that these
- // numbers can't change. I don't use \n and \r
- // because these values change between various C
- // compilers on the Mac.
-
- switch (ch) {
- case 13:
- switch (state) {
- case kWorkerWaitingForCR1:
- state = kWorkerWaitingForLF1;
- break;
- case kWorkerWaitingForCR2:
- state = kWorkerWaitingForLF2;
- break;
- default:
- state = kWorkerWaitingForCR1;
- break;
- }
- break;
- case 10:
- switch (state) {
- case kWorkerWaitingForLF1:
- state = kWorkerWaitingForCR2;
- break;
- case kWorkerWaitingForLF2:
- state = kWorkerDone;
- break;
- default:
- state = kWorkerWaitingForCR1;
- break;
- }
- break;
- default:
- state = kWorkerWaitingForCR1;
- break;
- }
- } else {
- err = bytesReceived;
- }
- } while ( err == noErr && state != kWorkerDone );
-
- if (err == noErr) {
- // Append the null terminator that turns the HTTP request into a C string.
- buffer[bufferIndex] = 0;
- }
-
- return (err);
- }
-
- /////////////////////////////////////////////////////////////////////
-
- static OSStatus CopyFileToEndpoint(const FSSpec *fileSpec, char *buffer, EndpointRef worker)
- // Copy the file denoted by fileSpec to the endpoint. buffer is a
- // temporary buffer of size kTransferBufferSize. Initially buffer
- // contains a C string that is the HTTP header to output. After that,
- // the routine uses buffer as temporary storage. We do this because
- // we want any errors opening the file to be noticed before we send
- // the header saying that the request went through successfully.
- {
- OSStatus err;
- OSStatus junk;
- long bytesToSend;
- long bytesThisTime;
- short fileRefNum;
-
- err = FSpOpenDF(fileSpec, fsRdPerm, &fileRefNum);
- if (err == noErr) {
- err = GetEOF(fileRefNum, &bytesToSend);
-
- // Write the HTTP header out to the endpoint.
-
- if (err == noErr) {
- err = OTSndQ(worker, buffer, OTStrLength(buffer));
- }
-
- // Copy the file in kTransferBufferSize chunks to the endpoint.
-
- while (err == noErr && bytesToSend > 0) {
- if (bytesToSend > kTransferBufferSize) {
- bytesThisTime = kTransferBufferSize;
- } else {
- bytesThisTime = bytesToSend;
- }
- err = FSReadQ(fileRefNum, bytesThisTime, buffer);
- if (err == noErr) {
- err = OTSndQ(worker, buffer, bytesThisTime);
- }
- bytesToSend -= bytesThisTime;
- }
-
- // Clean up.
- junk = FSClose(fileRefNum);
- OTAssert("WorkerThreadProc: Could not close file", junk == noErr);
- }
-
- return (err);
- }
-
- /////////////////////////////////////////////////////////////////////
-
- Handle gMovieHeader = nil;
- Handle gDummyFrame = nil;
- Handle gZero5000 = nil;
- //#define FRAMESIZE 5000
- #define FRAMESIZE 10000
-
- OSErr InitLiveMovie()
- {
- OSErr err;
-
- gMovieHeader = Get1Resource( 'moo ', 128 );
- if( !gMovieHeader ) {
- err = ResError();
- DebugStr( "\p couldn't get the movie header resource" );
- goto bail;
- }
- HLockHi( gMovieHeader );
-
- gZero5000 = NewHandleClear( FRAMESIZE );
- err = MemError();
- if( err ) {
- DebugStr( "\p couldn't allocate some zeros." );
- goto bail;
- }
- HLockHi( gZero5000 );
-
- gDummyFrame = Get1Resource( ' v', 128 );
- if( !gDummyFrame ) {
- err = ResError();
- DebugStr( "\p couldn't get the dummy frame" );
- goto bail;
- }
- DetachResource( gDummyFrame );
- SetHandleSize( gDummyFrame, FRAMESIZE );
- HLockHi( gDummyFrame );
- if( GetHandleSize( gDummyFrame ) != FRAMESIZE )
- DebugStr( "\p warning: dummy frame isn't 5000 bytes" );
-
- bail:
- return err;
- }
-
- /* ---------------------------------------------------------------------- */
-
- static pascal Boolean
- SeqGrabberModalFilterProc (DialogPtr theDialog, EventRecord *theEvent,
- short *itemHit, long refCon)
- {
- // Ordinarily, if we had multiple windows we cared about, we'd handle
- // updating them in here, but since we don't, we'll just clear out
- // any update events meant for us
-
- Boolean handled = false;
-
- if ((theEvent->what == updateEvt) &&
- ((WindowPtr) theEvent->message == (WindowPtr) refCon))
- {
- WindowPtr wPtr = (WindowPtr) refCon;
- BeginUpdate (wPtr);
- EndUpdate (wPtr);
- handled = true;
- }
- return (handled);
- }
-
- /* ---------------------------------------------------------------------- */
-
- pascal ComponentResult myAddFrameProc( SGChannel c, short bufferNum, TimeValue atTime, TimeScale scale, const SGCompressInfo *ci, long refCon );
-
- SeqGrabComponent gSeqGrabber = 0;
- SGChannel gVideoChannel = 0;
- WindowPtr gMonitor = 0;
- ICMAlignmentProcRecord gSeqGrabberAlignProc = {0,0};
- SGAddFrameBottleUPP gAddFrameProc = 0;
-
- OSErr InitGrabber()
- {
- OSErr err;
- SGModalFilterUPP seqGragModalFilterUPP = 0;
- VideoBottles vb;
- ComponentDescription cd;
- CompressorComponent cJPEG = 0;
-
- EnterMovies();
-
- err = OpenADefaultComponent( SeqGrabComponentType, 0, &gSeqGrabber );
- if( err ) { DebugStr( "\p open barg" ); goto bail; }
-
-
- gMonitor = GetNewDialog (129, nil, (WindowPtr)-1L);
- SetPort( gMonitor );
- SizeWindow( gMonitor, 320, 240, false );
- ShowWindow( gMonitor );
-
- err = SGInitialize( gSeqGrabber );
- if( err ) { DebugStr( "\p init barg" ); goto bail; }
-
- err = SGSetGWorld (gSeqGrabber, (CGrafPtr) gMonitor, nil);
- if( err ) { DebugStr( "\p sgsetgworld" ); goto bail; }
-
- err = SGNewChannel (gSeqGrabber, VideoMediaType, &gVideoChannel);
- if( err ) { DebugStr( "\p new video channel" ); goto bail; }
-
- err = SGSetChannelUsage (gVideoChannel, seqGrabRecord);
- if( err ) { DebugStr( "\p set channel usage" ); goto bail; }
-
- err = SGSetChannelBounds (gVideoChannel, &(gMonitor->portRect));
- if( err ) { DebugStr( "\p set channel bounds" ); goto bail; }
-
- err = SGGetAlignmentProc (gSeqGrabber, &gSeqGrabberAlignProc);
- if( err ) { DebugStr( "\p get align rect proc" ); goto bail; }
-
- #if 0
- err = SGSetVideoCompressorType(gVideoChannel, kJPEGCodecType );
- if( err ) { DebugStr( "\p SGSetVideoCompressorType" ); goto bail; }
- #else
- cd.componentType = compressorComponentType;
- cd.componentSubType = kJPEGCodecType;
- cd.componentManufacturer = 0;
- cd.componentFlags = 0;
- cd.componentFlagsMask = 0;
- cJPEG = FindNextComponent( 0, &cd );
- if( !cJPEG ) DebugStr( "\p can't find JPEG compressor!" );
-
- err = SGSetVideoCompressor(gVideoChannel, 24, cJPEG, codecMinQuality, 0, 0 );
- if( err ) { DebugStr( "\p SGSetVideoCompressor" ); goto bail; }
- #endif
-
- err = SGSetFrameRate( gVideoChannel, 4L<<16 ); // FPS is 4
- if( err ) { DebugStr( "\p SGSetFrameRate" ); goto bail; }
-
- gAddFrameProc = NewSGAddFrameBottleProc( myAddFrameProc );
-
- /* get the current bottlenecks */
- vb.procCount = 9;
- err = SGGetVideoBottlenecks (gVideoChannel, &vb);
- // vb.grabCompleteProc = myGrabFrameComplete;
- vb.addFrameProc = gAddFrameProc;
- err = SGSetVideoBottlenecks (gVideoChannel, &vb);
-
- seqGragModalFilterUPP = (SGModalFilterUPP)NewSGModalFilterProc(SeqGrabberModalFilterProc);
- err = SGSettingsDialog(gSeqGrabber, gVideoChannel, 0,
- nil, 0L, seqGragModalFilterUPP, (long)StripAddress ((Ptr) gMonitor));
- DisposeRoutineDescriptor(seqGragModalFilterUPP);
- if( err ) { DebugStr( "\p settings dialog" ); goto bail; }
-
- err = SGStartPreview (gSeqGrabber);
- if( err ) { DebugStr( "\p start preview" ); goto bail; }
-
- bail:
- return err;
- }
-
- OSErr IdleGrabber()
- {
- if (gSeqGrabber)
- SGIdle (gSeqGrabber);
- return noErr;
- }
-
- Boolean IsGrabberEvent( EventRecord *event )
- {
- switch (event->what) {
- case updateEvt:
- if ((gMonitor != nil) && ((WindowPtr) (event->message) == (WindowPtr) gMonitor))
- {
- SGUpdate(gSeqGrabber, ((WindowPeek)gMonitor)->updateRgn);
- BeginUpdate (gMonitor);
- EndUpdate (gMonitor);
- return true;
- }
- break;
- case mouseDown:
- {
- WindowPtr theWindow;
- short windowCode = MacFindWindow (event->where, &theWindow);
-
- if ( (windowCode == inDrag) && (theWindow == gMonitor) )
- {
- ComponentResult result = noErr;
- Rect limitRect;
- RgnHandle grayRgn = GetGrayRgn();
- Rect boundsRect;
-
- // Find bounds
- if (grayRgn != nil)
- {
- limitRect = (*grayRgn)->rgnBBox;
- }
- else
- {
- limitRect = qd.screenBits.bounds;
- }
-
- // Pause the sequence grabber
- result = SGPause (gSeqGrabber, true);
-
- if (gVideoChannel != nil)
- {
- // Drag it with the totally cool DragAlignedWindow
- result = SGGetChannelBounds (gVideoChannel, &boundsRect);
- DragAlignedWindow (theWindow, event->where, &limitRect, &boundsRect, &gSeqGrabberAlignProc);
- }
- else
- {
- DragWindow (theWindow, event->where, &limitRect);
- }
-
- // Start up the sequence grabber
- result = SGPause (gSeqGrabber, false);
-
- return true;
- }
- }
- break;
- }
- return false;
- }
-
- OSErr KillGrabber()
- {
- CloseComponent( gSeqGrabber );
- gSeqGrabber = 0;
-
- if( gMonitor )
- DisposeDialog( gMonitor );
- gMonitor = 0;
-
- return noErr;
- }
-
- EndpointRef gGrabberEndpoint = 0;
- long gFrameCount = 0;
- OSErr gGrabberErr = noErr;
-
- static OSErr StartGrabbing( EndpointRef worker )
- {
- OSErr err;
-
- if( gGrabberEndpoint ) {
- DebugStr( "\p already in use!" );
- return paramErr; // (lame)
- }
- gGrabberEndpoint = worker;
- gGrabberErr = noErr;
-
- err = SGSetDataOutput( gSeqGrabber, nil, seqGrabDontMakeMovie );
- if (err) {
- DebugStr( "\p SGSetDataOutput failed." );
- goto bail;
- }
-
- err = SGStartRecord(gSeqGrabber);
- if (err) {
- DebugStr( "\p SGStartRecord failed." );
- goto bail;
- }
-
- bail:
- return err;
- }
-
- #define TRACK_BIGGEST_FRAME 1
- #if TRACK_BIGGEST_FRAME
- long gBiggestFrame = 0;
- #endif // TRACK_BIGGEST_FRAME
-
- pascal ComponentResult myAddFrameProc( SGChannel c, short bufferNum,
- TimeValue atTime, TimeScale scale, const SGCompressInfo *ci, long refCon )
- {
- OSErr err;
-
- if( !gGrabberEndpoint || gGrabberErr )
- return noErr;
-
- if( ci->bufferSize > FRAMESIZE ) {
- #if TRACK_BIGGEST_FRAME
- if( gBiggestFrame < ci->bufferSize ) {
- Str255 str;
- gBiggestFrame = ci->bufferSize;
-
- NumToString( gBiggestFrame, str );
- str[++str[0]] = ';';
- str[++str[0]] = 'g';
- DebugStr( str );
- }
- #endif // TRACK_BIGGEST_FRAME
-
- // too big. fall back to dummy frame.
- err = OTSndQ( gGrabberEndpoint, *gDummyFrame, GetHandleSize( gDummyFrame ) );
- }
- else {
- // small enough. send it along.
- err = OTSndQ( gGrabberEndpoint, ci->buffer, ci->bufferSize );
- if( err ) goto bail;
- err = OTSndQ( gGrabberEndpoint, *gZero5000, FRAMESIZE - ci->bufferSize );
- }
-
- bail:
- if( err )
- gGrabberErr = err;
- return err;
- }
-
- static OSErr StopGrabbing( EndpointRef worker )
- {
- OSErr err;
-
- if( gGrabberEndpoint == worker ) {
- gGrabberEndpoint = 0;
-
- //••
- err = SGStop(gSeqGrabber);
- err = SGStartPreview(gSeqGrabber);
- }
- return noErr;
- }
-
- static OSStatus CopyLiveMovieToEndpoint(char *buffer, EndpointRef worker)
- {
- OSStatus err;
- // long i;
-
- // Write the HTTP header out to the endpoint.
-
- err = OTSndQ( worker, buffer, OTStrLength(buffer) );
- if( err ) goto bail;
-
- // Write the movie header.
- err = OTSndQ( worker, *gMovieHeader, GetHandleSize(gMovieHeader) );
- if( err ) goto bail;
-
- YieldToAnyThread();
-
- err = StartGrabbing( worker );
- if( err ) goto bail;
-
- // Write movie frames.
- gFrameCount = 0;
- while( (gFrameCount < 1500) && gGrabberEndpoint && !gGrabberErr ) {
- YieldToAnyThread();
- }
- /*
- for( i = 0; i < 1500; i++ ) {
- Size size = GetHandleSize( gDummyFrame );
- //••
-
- if( size != FRAMESIZE )
- DebugStr( "\p warning: dummy frame isn't 5000 bytes" );
-
- err = OTSndQ( worker, *gDummyFrame, size );
- if( err ) goto bail;
-
- YieldToAnyThread();
- }
- */
-
- bail:
- StopGrabbing( worker );
-
- return err;
- }
-
- // demo movie with just dummy frames.
- static OSStatus CopyOliveMovieToEndpoint(char *buffer, EndpointRef worker)
- {
- OSStatus err;
- long i;
-
- // Write the HTTP header out to the endpoint.
-
- err = OTSndQ( worker, buffer, OTStrLength(buffer) );
- if( err ) goto bail;
-
- // Write the movie header.
- err = OTSndQ( worker, *gMovieHeader, GetHandleSize(gMovieHeader) );
- if( err ) goto bail;
-
- YieldToAnyThread();
-
- // Write movie frames.
- for( i = 0; i < 1500; i++ ) {
- Size size = GetHandleSize( gDummyFrame );
-
- if( size != FRAMESIZE )
- DebugStr( "\p warning: dummy frame isn't 5000 bytes" );
-
- err = OTSndQ( worker, *gDummyFrame, size );
- if( err ) goto bail;
-
- YieldToAnyThread();
- }
-
- bail:
- return err;
- }
-
- /////////////////////////////////////////////////////////////////////
-
- static pascal OSStatus WorkerThreadProc(WorkerContextPtr context)
- // This routine is the starting routine for the worker thread.
- // The thread is responsible for reading an HTTP request from
- // an endpoint, processing the requesting and writing the results
- // back to the endpoint.
- {
- OSStatus err;
- OSStatus junk;
- char *buffer = nil;
- char *errStr;
- char fileName[256];
- char mimeType[256];
- FSSpec fileSpec;
-
- printf("WorkerThreadProc: Starting\n");
- fflush(stdout);
- OTAssert("WorkerThreadProc: Context is nil!", context != nil);
- OTAssert("WorkerThreadProc: Worker endpoint is nil!", context->worker != nil);
-
- // Allocate the transfer buffer in the heap.
-
- err = noErr;
- buffer = OTAllocMem(kTransferBufferSize);
- if (buffer == nil) {
- err = kENOMEMErr;
- }
-
- // Read the request into the transfer buffer.
-
- if (err == noErr) {
- err = ReadHTTPRequest(context->worker, buffer);
- }
-
- if (err == noErr) {
-
- // Get the requested file name (and it's mimeType) from the
- // HTTP request in the transfer buffer.
-
- err = ExtractRequestedFileName(buffer, fileName, mimeType);
-
- if (err == noErr) {
-
- // Create the appropriate HTTP response in the buffer.
-
- sprintf(buffer, gHTTPHeader, mimeType);
-
- // Copy the file (with preceding HTTP header) to the endpoint.
-
- if( 0 == strcmp( fileName, "live.mov" ) ) {
- err = CopyLiveMovieToEndpoint( buffer, context->worker );
- }
- else if( 0 == strcmp( fileName, "olive.mov" ) ) {
- err = CopyOliveMovieToEndpoint( buffer, context->worker );
- }
- else {
- (void) FSMakeFSSpec(context->vRefNum, context->dirID, C2PStr(fileName), &fileSpec);
- err = CopyFileToEndpoint(&fileSpec, buffer, context->worker);
- }
- }
-
- // Handle any errors by sending back an appropriate error header.
-
- if (err != noErr) {
- switch (err) {
- case fnfErr:
- errStr = "File Not Found";
- break;
- default:
- errStr = "Unknown Error";
- break;
- }
- sprintf(buffer, gDefaultOutputText, err, errStr);
- err = OTSndQ(context->worker, buffer, OTStrLength(buffer));
- }
- }
-
- // Shut down the endpoint and clean up the WorkerContext.
-
- if (err == noErr) {
- err = OTSndOrderlyDisconnect(context->worker);
- if (err == noErr) {
- err = OTRcvOrderlyDisconnect(context->worker);
- }
- }
-
- junk = OTCloseProvider(context->worker);
- OTAssert("StartHTTPServer: Could not close listener", junk == noErr);
-
- OTFreeMem(context);
-
- if (buffer != nil) {
- OTFreeMem(buffer);
- }
-
- printf("WorkerThreadProc: Stopping with final result %d.\n", err);
- fflush(stdout);
-
- return (noErr);
- }
-
- /////////////////////////////////////////////////////////////////////
-
- OSStatus RunHTTPServer(InetHost ipAddr, short vRefNum, long dirID)
- // This routine runs an HTTP server. It doesn't return until
- // someone sets gQuitNow, so you should most probably call this
- // routine on its own thread. ipAddr is the IP address that
- // the server listens on. Specify kOTAnyInetAddress to listen
- // on all IP addresses on the machine; specify an IP address
- // to listen on a specific address. vRefNum and dirID point
- // to the root directory of the HTTP information to be served.
- //
- // The routine creates a listening endpoint and listens for connection
- // requests on that endpoint. When a connection request arrives, it creates
- // a new worker thread (with accompanying endpoint) and accepts the connection
- // on that thread.
- //
- // Note the use of the "tilisten" module which prevents multiple
- // simultaneous T_LISTEN events coming from the transport provider,
- // thereby greatly simplifying the listen/accept sequence.
- {
- OSStatus err;
- EndpointRef listener;
- TBind bindReq;
- InetAddress ipAddress;
- InetAddress remoteIPAddress;
- TCall call;
- ThreadID workerThread;
- OSStatus junk;
- WorkerContextPtr workerContext;
- TEndpointInfo Info;
- char buf[128];
-
- // display IP address in String
- OTInetHostToString(ipAddr, buf);
- printf("HTTP Server on <%s> Starting.\n", buf);
-
- fflush(stdout);
-
- // Create the listen endpoint.
-
- // RRK comments added 8/27/97
- // In order for the IP address to be re-used after quitting this sample program and
- // restarting it, the "ReuseAddr" option must be set.
- // One should be able to set the "ReuseAddr" option as part of the configuration string
- // however, if you try to do this along with specifying the use of the tilisten
- // module, the following code hangs the system hard. As an alternative, the
- // endpoint is created with the tilisten module layered above tcp. After that the
- // OTOptionManagement call is made to set this option.
- // RRK Comments end
-
- //listener = OTOpenEndpoint(OTCreateConfiguration("tilisten,tcp(ReuseAddr=1)"), 0, nil, &err);
- listener = OTOpenEndpoint(OTCreateConfiguration("tilisten,tcp"), 0, &Info, &err);
-
- // RRK addition 8/27/97
- // set the ReuseAddr option
- if (err == noErr) {
- junk = DoNegotiateIPReuseAddrOption(listener, true);
- OTAssert("Unable to negotiate raw mode for listener endpoint", junk == noErr);
- }
- // end RRK addition 8/27/97
-
- // Set the endpoint mode and bind it to the appropriate IP address.
-
- if (err == noErr) {
- SetDefaultEndpointModes(listener);
- OTInitInetAddress(&ipAddress, 80, ipAddr); // port & host ip
- bindReq.addr.buf = (UInt8 *) &ipAddress;
- bindReq.addr.len = sizeof(ipAddress);
- bindReq.qlen = 1;
- err = OTBind(listener, &bindReq, nil);
- }
-
- while (err == noErr) {
-
- // Listen for connection attempts...
-
- OTMemzero(&call, sizeof(TCall));
- call.addr.buf = (UInt8 *) &remoteIPAddress;
- call.addr.maxlen = sizeof(remoteIPAddress);
- err = OTListen(listener, &call);
-
- // ... then spool a worker thread for this connection.
-
- if (err == noErr) {
-
- // Create the worker context.
-
- workerThread = kNoThreadID;
- workerContext = OTAllocMem(sizeof(WorkerContext));
- if (workerContext == nil) {
- err = kENOMEMErr;
- } else {
- workerContext->worker = nil;
- workerContext->vRefNum = vRefNum;
- workerContext->dirID = dirID;
- }
-
- // Open the worker endpoint.
-
- if (err == noErr) {
- workerContext->worker = OTOpenEndpoint(OTCreateConfiguration("tcp"), 0, nil, &err);
- if (err == noErr) {
- SetDefaultEndpointModes(workerContext->worker);
- }
- }
-
- // Create the worker thread.
-
- if (err == noErr) {
- err = NewThread(kCooperativeThread,
- (ThreadEntryProcPtr) WorkerThreadProc, workerContext,
- 0, kNewSuspend | kCreateIfNeeded,
- nil,
- &workerThread);
- }
-
- // Accept the connection on the thread.
-
- if (err == noErr) {
- err = OTAccept(listener, workerContext->worker, &call);
- }
-
- // Schedule the thread for execution.
-
- if (err == noErr) {
- err = SetThreadState(workerThread, kReadyThreadState, kNoThreadID);
- }
-
- // Clean up on error.
-
- if (err != noErr) {
- if (workerContext != nil) {
- if (workerContext->worker != nil) {
- junk = OTCloseProvider(workerContext->worker);
- OTAssert("StartHTTPServer: Could not close worker", junk == noErr);
- }
- OTFreeMem(workerContext);
- }
- if (workerThread != kNoThreadID) {
- junk = DisposeThread(workerThread, nil, true);
- OTAssert("StartHTTPServer: DisposeThread failed", junk == noErr);
- }
- printf("StartHTTPServer: Failed to spool worker, error %d.\n", err);
- fflush(stdout);
- err = noErr;
- }
- }
- }
-
- // Clean up the listener endpoint.
-
- if (listener != nil) {
- junk = OTCloseProvider(listener);
- OTAssert("StartHTTPServer: Could not close listener", junk == noErr);
- }
-
- printf("HTTP Server on %08x: Stopping.\n", ipAddr);
- fflush(stdout);
-
- return (err);
- }
-